import { Toucher } from './Toucher.js';

/** @类 观察者 */
export class Observer {
  constructor() {
    this.listeners = {};
  }

  /** @方法 添加监听 */
  addListener(type, listener) {
    if (!this.listeners[type]) {
      this.listeners[type] = [];
    }
    this.listeners[type].push(listener);
  }

  /** @方法 移除监听 */
  removeListener(type, listener) {
    if (!this.listeners[type]) {
      return;
    }
    const index = this.listeners[type].indexOf(listener);
    if (index > -1) {
      this.listeners[type].splice(index, 1);
    }
  }

  /** @方法 触发监听 */
  dispatch(type, ...args) {
    this.listeners[type] && this.listeners[type].forEach((listener) => listener(...args));
  }
}

/** @类 渲染者 */
export class Renderer {
  constructor(el) {
    this.el = el;
    this.initSize();
    this.initBuffer();
    this.initCanvas();
    this.observer = new Observer();
    this.observer.addListener('draw', this.onDraw.bind(this));
    this.observer.addListener('zoom', this.onZoom.bind(this));
    this.observer.addListener('drag', this.onDrag.bind(this));
    this.observer.addListener('click', this.onClick.bind(this));
  }

  /** @辅助 初始化尺寸 */
  initSize() {
    const { width, height } = this.el.getBoundingClientRect();
    this.width = width;
    this.height = height;
  }

  /** @辅助 初始化画板 */
  initCanvas() {
    const { width, height } = this;
    const canvas = document.createElement('canvas');
    canvas.style.width = '100%';
    canvas.style.height = '100%';
    canvas.width = width;
    canvas.height = height;
    this.canvas = canvas;
    this.canvasCtx = canvas.getContext('2d');
    this.toucher = new Toucher(this);
    this.el.firstChild && this.el.removeChild(this.el.firstChild);
    this.el.appendChild(canvas);
    // 开启渲染循环
    const requestDrawFrame = () => {
      this.parser && this.observer.dispatch('draw');
      requestAnimationFrame(requestDrawFrame);
    };
    requestDrawFrame();
  }

  /** @辅助 初始化缓存 */
  initBuffer() {
    this.buffer = document.createElement('canvas');
    this.bufferCtx = this.buffer.getContext('2d');
  }

  /** @事件 渲染画面 */
  onDraw() {
    const { parser, canvas, buffer, bufferCtx, canvasCtx } = this;
    const { background, scale } = parser;
    const scaling = scale.scaling * scale.rate; // 其中 scale.rate = 画布宽度 / 背景宽度
    // 绘制背景图 to 缓存区
    bufferCtx.drawImage(
      background.image,
      0,
      0,
      buffer.width,
      buffer.height,
    );
    // 绘制点击区 to 缓存区
    // 绘制缓存区 to 画板区
    canvasCtx.drawImage(
      buffer,
      background.offsetX / scaling, // 源图像的 x 坐标
      background.offsetY / scaling, // 源图像的 y 坐标
      canvas.width / scaling, // 源图像的宽度
      canvas.height / scaling, // 源图像的高度
      0, // 目标图像的 x 坐标
      0, // 目标图像的 y 坐标
      canvas.width, // 目标图像的宽度
      canvas.height // 目标图像的高度
    ); 
  }

  /** @事件 触发拖动 */
  onDrag({ point, downPoint, downOffset }) {
    const { canvas, parser } = this;
    const { scale, background } = parser;
    const scaling = scale.scaling * scale.rate;
    // 计算偏移位置
    const newOffset = {
      x: downOffset.x + (downPoint.x - point.x),
      y: downOffset.y + (downPoint.y - point.y),
    };
    // 限制偏移边界（目前边界没法限制）
    newOffset.x = Math.min(Math.max(newOffset.x, 0), background.width - (canvas.width / scaling));
    newOffset.y = Math.min(Math.max(newOffset.y, 0), background.height - (canvas.height * scaling));
    // 更新偏移位置
    background.offsetX = newOffset.x;
    background.offsetY = newOffset.y;
  }

  /** @事件 放大缩小 */
  onZoom({ zoom }) {
    const { canvas } = this;
    const { scale, background } = this.parser;
    let newScaling;
    // 计算缩放比例
    if (zoom) {
      newScaling = scale.scaling + scale.step;
    } else {
      newScaling = scale.scaling - scale.step;
    }
    // 限制缩放比例
    newScaling = Math.max(scale.min, Math.min(newScaling, scale.max));

    // 计算偏移位置（缩放存在问题）
    const oldSize = {
      width: canvas.width / scale.scaling,
      height: canvas.height / scale.scaling,
    };
    const newSize = {
      width: canvas.width / newScaling,
      height: canvas.height / newScaling,
    };
    // 计算offsetX, offsetY
    const newOffset = {
      x: background.offsetX + (((oldSize.width - newSize.width) / 2) * scale.scaling),
      y: background.offsetX + (((oldSize.height - newSize.height) / 2) * scale.scaling),
    };
    // 限制偏移边界（边界目前无法限制）
    newOffset.x = Math.min(Math.max(newOffset.x, 0), background.width - canvas.width / newScaling);
    newOffset.y = Math.min(Math.max(newOffset.y, 0), background.height - canvas.height / newScaling);

    // 更新缩放比例
    scale.scaling = newScaling;
    // 更新偏移位置
    background.offsetX = newOffset.x;
    background.offsetY = newOffset.y;
  }

  /** @事件 点击触发 */
  onClick({ point }) {
    console.log('click', point);
  }

  /** @方法 销毁内容 */
  destroy() {
    this.toucher.destroy();
    this.observer.removeListener('draw', this.onDraw.bind(this));
    this.observer.removeListener('zoom', this.onZoom.bind(this));
    this.observer.removeListener('drag', this.onDrag.bind(this));
    this.observer.removeListener('click', this.onClick.bind(this));
  }

  /** @方法 加载项目 */
  async load(parser) {
    await parser.load(this);
    this.parser = parser;
    this.buffer.width = parser.background.width;
    this.buffer.height = parser.background.height;
  }
}

export default Renderer;
